import { Context } from 'egg'

const dayjs = require('dayjs')
const dayjsUTC = require('dayjs/plugin/utc')

dayjs.extend(dayjsUTC)

const crypto = require('crypto')

export function getRealIp(ctx: Context): string {
  let ip = ctx.request.ip || ''

  if (Array.isArray(ctx.request.ips)) {
    ip = ctx.request.ips.pop() || ip
  }

  ip = ctx.get('X-Real-IP') || ip

  return ip.replace(/::ffff:/, '')
}

export function getTimestamptz(utc) {
  if (!utc) return utc

  const date = dayjs(utc)

  if (date.isValid()) {
    const timestamp = date.valueOf()
    const timestamptz = timestamp - (new Date()).getTimezoneOffset() * 6e4

    return timestamptz
  }
}

export function getTimestamp(tz) {
  if (!tz) return tz

  const date = dayjs(tz)

  if (date.isValid()) {
    const timestamptz = date.valueOf()
    const timestamp = timestamptz + (new Date()).getTimezoneOffset() * 6e4

    return timestamp
  }
}

export function getCurrentDate(offset = 0, unit = 'day') {
  // pg CURRENT_DATE
  const current = dayjs()

  return (current.add(offset, unit))
    .startOf(unit)
    // TODO
    .utc()
    .format()
}

export function decrypt({
  session_key,
  encryptedData,
  iv
}, appid) {
  // base64 decode
  const sessionKey = Buffer.from(session_key, 'base64')

  encryptedData = Buffer.from(encryptedData, 'base64')
  iv = Buffer.from(iv, 'base64')

  let decoded

  try {
    const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)

    decipher.setAutoPadding(true)

    decoded = decipher.update(encryptedData, 'binary', 'utf8')
    decoded += decipher.final('utf8')
    decoded = JSON.parse(decoded)
  } catch (err) {
    throw new Error('Illegal Buffer')
  }

  if (decoded && decoded.watermark.appid !== appid) {
    throw new Error('Illegal Buffer')
  }

  return decoded
}

export function getValue(o, key) {
  return key
    .replace(/\.+/g, '.')
    .split('.')
    .reduce((p, n) => {
      const steps = []

      if (typeof n === 'string') {
        n = n.replace(/\[(\d+)\]/g, (str, match) => {
          steps.push(match)
          return ''
        })

        try {
          p = p[n]
        } catch (err) {}
      }

      while (steps.length) {
        p = p || {}
        p = p[steps.shift()]
      }

      return p
    }, o)
}

export function hasKeyIn(o, keys = []) {
  return keys.some(key => {
    return getValue(o, key) !== undefined
  })
}

export function getContentCleaned(html) {
  if (Array.isArray(html)) {
    html = html.join(' ')
  }

  if (typeof html !== 'string') return ''

  try {
    return html
      // remove html entities
      .replace(/&\w+;\s*/g, '')
      // remove html tags
      .replace(/(<([^>]+)>)/gi, ' ')
  } catch (err) {}

  return ''
}

export function streamToBuffer(stream) {
  return new Promise((resolve, reject) => {
    const buffers = []

    stream.on('error', reject)
    stream.on('data', (data) => buffers.push(data))
    stream.on('end', () => resolve(Buffer.concat(buffers)))
  })
}
